using System.Reflection;

namespace Synapse.Framework.AoP
{
    internal class Invocation : IInvocation 
    {
        private readonly IExecuteBehaviour executeBehaviour;
        private readonly IExecuteCheck executeCheck;

        public Invocation(object instance, MethodInfo methodInfo, object[] parameters)
            : this(instance, methodInfo, parameters, new ExecuteBehaviour(instance), new ExecuteCheck(instance))
        {
        }
        
        public Invocation(object instance, MethodInfo methodInfo, object[] parameters,IExecuteBehaviour executeBehaviour, IExecuteCheck executeCheck )
        {
            Instance = instance;
            MethodInfo = methodInfo;
            Parameters = parameters;
            this.executeBehaviour = executeBehaviour;
            this.executeCheck = executeCheck;
        }

        public object Instance { get; private set; }
        public MethodInfo MethodInfo { get; private set; }
        public object[] Parameters { get; private set; }
        public object ReturnValue { get; set; }

        public void InvokeMethod()
        {
            ReturnValue = ExcuteMethod(MethodInfo,Parameters);
        }

        private object ExcuteMethod(MethodInfo methodInfo, object[] parameters)
        {
            executeBehaviour.BeforeCallMethod(methodInfo, parameters);
            executeCheck.ForParameters(methodInfo, parameters);
            var returnValue = methodInfo.Invoke(Instance, parameters);
            executeCheck.ForReturnValue(methodInfo, returnValue);
            executeBehaviour.AfterCallMethod(methodInfo, parameters, returnValue);
            return returnValue;
        }
    }
}